home *** CD-ROM | disk | FTP | other *** search
- /*********************************************************************
- Project : MacPerl - Standalone Perl
- File : MPConsole.cp - Console interface for GUSI
- Author : Matthias Neeracher
- Language : MPW C/C++
-
- $Log: MPConsole.cp,v $
- *********************************************************************/
-
- #include <GUSIFile_P.h>
-
- #include "MPGlobals.h"
- #include "MPAEVTStream.h"
- #include "MPConsole.h"
-
- #include <AEStream_CPlus.h>
-
- extern "C" {
- #include <AESubDescs.h>
-
- #include <ioctl.h>
- }
-
- class MPAEVTSocket; // That's what this file's all about
-
- class MPAEVTSocket : public Socket {
- friend class MPAEVTSocketDomain;
-
- MPAEVTSocket(OSType key, Boolean input, Boolean output);
-
- virtual ~MPAEVTSocket();
-
- OSType key;
- Handle inData;
- Handle outData;
- Boolean eof;
- Boolean nonblocking;
- Boolean needy;
- MPAEVTSocket * next;
- MPAEVTSocket * prev;
- public:
- virtual int read(void * buffer, int buflen);
- virtual int write(void * buffer, int buflen);
- virtual int fcntl(unsigned int cmd, int arg);
- virtual void pre_select(Boolean wantRead, Boolean wantWrite, Boolean wantExcept);
- virtual int select(Boolean * canRead, Boolean * canWrite, Boolean * exception);
- virtual int ioctl(unsigned int request, void *argp);
- virtual int isatty();
- };
-
- class MPAEVTSocketDomain : public FileSocketDomain {
- friend class MPAEVTSocket;
-
- MPAEVTSocket * Lookup(OSType key, Boolean input, Boolean output);
-
- MPAEVTSocket * first;
- OSType key;
- OSType mode;
- DescType saseClass;
- DescType saseID;
- AppleEvent sase;
- AEDesc target;
- AEStream outputData;
- AEDesc outputDirect;
- short outputDataCount;
- public:
- MPAEVTSocketDomain();
-
- virtual Boolean Yours(const GUSIFileRef & ref, Request request);
- virtual Socket * open(const GUSIFileRef & ref, int oflag);
-
- void CollectOutput(AppleEvent * output);
- void DistributeInput(const AppleEvent * input, long mode);
- void KillInput();
-
- void DoRead();
- Boolean MayRead();
-
- Boolean finish;
- long received;
- };
-
- MPAEVTSocketDomain MPAEVTSockets;
-
- #if !defined(powerc) && !defined(__powerc)
- #pragma segment MPAEVT
- #endif
-
- /************************ MPAEVTSocket members ************************/
-
- MPAEVTSocket::MPAEVTSocket(OSType key, Boolean input, Boolean output)
- : key(key)
- {
- eof = !input || MPAEVTSockets.mode == 'BATC';
- inData = input ? NewHandle(0) : nil;
- outData = output ? NewHandle(0) : nil;
-
- if (!MPAEVTSockets.first) {
- MPAEVTSockets.first = this;
- next = this;
- prev = this;
- } else {
- next = MPAEVTSockets.first;
- prev = MPAEVTSockets.first->prev;
- next->prev = this;
- prev->next = this;
- }
- }
-
- MPAEVTSocket::~MPAEVTSocket()
- {
- if (outData)
- if (GetHandleSize(outData)) {
- AEDesc desc ;
-
- desc.descriptorType = 'TEXT';
- desc.dataHandle = outData;
-
- if (key == '----')
- MPAEVTSockets.outputDirect = desc;
- else {
- MPAEVTSockets.outputData.WriteKey(key);
- MPAEVTSockets.outputData.WriteDesc(desc);
- ++MPAEVTSockets.outputDataCount;
- AEDisposeDesc(&desc);
- }
- } else
- DisposeHandle(outData);
-
- if (inData)
- DisposeHandle(inData);
-
- if (next == this)
- MPAEVTSockets.first = nil;
- else {
- MPAEVTSockets.first = next;
- next->prev = prev;
- prev->next = next;
- }
- }
-
- int MPAEVTSocket::fcntl(unsigned int cmd, int arg)
- {
- switch (cmd) {
- case F_GETFL:
- if (nonblocking)
- return FNDELAY;
- else
- return 0;
- case F_SETFL:
- if (arg & FNDELAY)
- nonblocking = true;
- else
- nonblocking = false;
-
- return 0;
- default:
- return GUSI_error(EOPNOTSUPP);
- }
- }
-
- int MPAEVTSocket::ioctl(unsigned int request, void *argp)
- {
- switch (request) {
- case FIONBIO:
- nonblocking = (Boolean) *(long *) argp;
-
- return 0;
- case FIONREAD:
- if (!inData)
- return GUSI_error(ESHUTDOWN);
-
- *(unsigned long *) argp = GetHandleSize(inData);
-
- return 0;
- case FIOINTERACTIVE:
- return 0;
- default:
- return GUSI_error(EOPNOTSUPP);
- }
- }
-
- int MPAEVTSocket::read(void * buffer, int buflen)
- {
- if (!inData)
- return GUSI_error(ESHUTDOWN);
-
- int avail;
-
- while (1) {
- avail = int(GetHandleSize(inData));
-
- if (!avail)
- if (eof)
- return 0;
- else {
- needy = true;
- if (nonblocking)
- return GUSI_error(EWOULDBLOCK);
- else
- MPAEVTSockets.DoRead();
- }
- else
- break;
- }
-
- needy = false;
- buflen = min(avail, buflen);
-
- HLock(inData);
- memcpy(buffer, *inData, buflen);
- if (avail -= buflen)
- memcpy(*inData, *inData+buflen, avail);
- HUnlock(inData);
- SetHandleSize(inData, avail);
-
- return buflen;
- }
-
- int MPAEVTSocket::write(void * buffer, int buflen)
- {
- if (!outData)
- return GUSI_error(ESHUTDOWN);
- else if (PtrAndHand(buffer, outData, buflen))
- return GUSI_error(ENOMEM);
-
- return buflen;
- }
-
- void MPAEVTSocket::pre_select(Boolean, Boolean, Boolean)
- {
- needy = false;
- }
-
- int MPAEVTSocket::select(Boolean * canRead, Boolean * canWrite, Boolean * exception)
- {
- int goodies = 0;
-
- if (canRead)
- if (inData)
- if (*canRead = (GetHandleSize(inData) > 0 || eof))
- ++goodies;
- else if (needy)
- MPAEVTSockets.DoRead();
- else
- needy = true;
- else
- *canRead = false;
-
- if (canWrite)
- if (*canWrite = (outData != nil))
- ++goodies;
-
- if (exception)
- *exception = false;
-
- return goodies;
- }
-
- int MPAEVTSocket::isatty()
- {
- return 1;
- }
-
- /********************* MPAEVTSocketDomain members **********************/
-
- #if !defined(powerc) && !defined(__powerc)
- #pragma force_active on
- #endif
-
- MPAEVTSocketDomain::MPAEVTSocketDomain()
- : FileSocketDomain(AF_UNSPEC, true, false), finish(false), first(nil), received(0)
- {
- outputData.OpenRecord(typeAERecord);
- }
-
- Boolean MPAEVTSocketDomain::Yours(const GUSIFileRef & ref, FileSocketDomain::Request request)
- {
- if (ref.spec || (request != willOpen))
- return false;
-
- if (
- (ref.name[4] | 0x20) == 's'
- && (ref.name[5] | 0x20) == 't'
- && (ref.name[6] | 0x20) == 'd'
- ) {
- if (!gRemoteControl)
- return false;
-
- switch (ref.name[7] | 0x20) {
- case 'i':
- key = '----';
-
- return (ref.name[8] | 0x20) == 'n' && !ref.name[9];
- case 'o':
- key = '----';
-
- return (ref.name[8] | 0x20) == 'u'
- && (ref.name[9] | 0x20) == 't'
- && !ref.name[10];
- case 'e':
- key = 'diag';
-
- return (ref.name[8] | 0x20) == 'r'
- && (ref.name[9] | 0x20) == 'r'
- && !ref.name[10];
- default:
- return false;
- }
- } else if (!strncmp(ref.name+4, "AEVT", 4))
- switch (ref.name[8]) {
- case 0:
- key = '----';
-
- return true;
- case ':':
- key = ' ';
-
- int len = strlen(ref.name+9);
-
- memcpy(&key, ref.name+9, len < 4 ? len : 4);
-
- return true;
- }
-
- return false;
- }
-
- Socket * MPAEVTSocketDomain::open(const GUSIFileRef &, int flags)
- {
- return Lookup(key, !(flags & O_WRONLY), (flags & 3) != 0);
- }
-
- Boolean MPAEVTSocketDomain::MayRead()
- {
- return mode == 'RCTL';
- }
-
- void MPAEVTSocketDomain::DoRead()
- {
- if (!MayRead())
- KillInput();
- else {
- long oldReceived = received;
-
- if (sase.dataHandle) {
- /* Send tickle event. Errors are probably deadly, as they case MacPerl to hang */
- AppleEvent tickle;
-
- if (AECreateAppleEvent(saseClass, saseID, &target, 0, 0, &tickle))
- goto waitForData;
-
- AEKeyword key;
- AEDesc desc;
-
- for (long index = 1; !AEGetNthDesc(&sase, index++, typeWildCard, &key, &desc);) {
- AEPutParamDesc(&tickle, key, &desc);
- AEDisposeDesc(&desc);
- }
-
- AESend(&tickle, &desc, kAENoReply, kAENormalPriority, kAEDefaultTimeout, nil, nil);
-
- AEDisposeDesc(&tickle);
- }
- waitForData:
- while (!MPConsoleSpin(SP_STREAM_READ, 0) && oldReceived == received)
- ;
- }
- }
-
- static void AEGetAttrOrParam(AEDesc * from, DescType keyword, void * into)
- {
- Size size;
- DescType type;
-
- if (AEGetAttributePtr(from, keyword, typeWildCard, &type, into, 4, &size))
- if (!AEGetParamPtr(from, keyword, typeWildCard, &type, into, 4, &size))
- AEDeleteParam(from, keyword);
- }
-
- void MPAEVTSocketDomain::DistributeInput(const AppleEvent * input, long mode)
- {
- AEDesc desc;
- AESubDesc aes;
- AESubDesc item;
- DescType keyword;
-
- if (mode)
- this->mode = mode;
-
- if (!AEGetParamDesc(input, 'INPT', typeAERecord, &desc)) {
- AEDescToSubDesc(&desc, &aes);
-
- HLock(aes.dataHandle);
-
- long maxIndex = AECountSubDescItems(&aes);
-
- for (long index = 0; index++ < maxIndex; ) {
- if (AEGetNthSubDesc(&aes, index, &keyword, &item))
- continue;
-
- long length;
-
- void * data = AEGetSubDescData(&item, &length);
- MPAEVTSocket * sock = Lookup(keyword, true, false);
-
- if (sock)
- if (AEGetSubDescType(&item) == typeNull)
- sock->eof = true;
- else if (sock->inData && length)
- PtrAndHand(data, sock->inData, length);
- }
-
- AEDisposeDesc(&desc);
- }
-
- if (!mode) {
- if (!AEGetParamDesc(input, '----', typeWildCard, &desc)) {
- MPAEVTSocket * sock = Lookup('----', true, false);
-
- if (sock)
- if (desc.descriptorType == typeNull)
- sock->eof = true;
- else if (sock->inData) {
- HLock(desc.dataHandle);
- HandAndHand(desc.dataHandle, sock->inData);
- }
-
- AEDisposeDesc(&desc);
- }
- } else {
- desc = sase;
-
- if (AEGetParamDesc(input, 'SASE', typeAERecord, &sase))
- sase = desc;
- else {
- if (desc.dataHandle)
- AEDisposeDesc(&desc);
- desc = target;
- if (AEGetAttributeDesc(input, keyAddressAttr, typeWildCard, &target))
- target = desc;
- else if (desc.dataHandle)
- AEDisposeDesc(&desc);
-
- AEGetAttrOrParam(&sase, keyEventClassAttr, &saseClass);
- AEGetAttrOrParam(&sase, keyEventIDAttr, &saseID);
- }
- }
- }
-
- void MPAEVTSocketDomain::KillInput()
- {
- int runs = 0;
- MPAEVTSocket * sock = first;
-
- if (sock)
- while ((runs += sock == first) < 2) {
- if (sock->needy)
- sock->eof = true;
-
- sock = sock->next;
- }
- }
-
- void MPAEVTSocketDomain::CollectOutput(AppleEvent * output)
- {
- OSErr err;
- AEStream want;
- AEDesc desc;
- int wantCount = 0;
- int runs = 0;
- MPAEVTSocket * sock = first;
-
- desc.descriptorType = 'TEXT';
-
- want.OpenList();
-
- if (sock)
- while ((runs += sock == first) < 2) {
- if (sock->outData) {
- desc.dataHandle = sock->outData;
- if (sock->key == '----')
- if (!AEPutParamDesc(output, '----', &desc))
- SetHandleSize(sock->outData, 0);
- else
- return; // This is sort of disastrous
- else if (outputData.WriteKey(sock->key) || outputData.WriteDesc(desc))
- return; // ... so is this.
- else
- ++outputDataCount;
- }
- if (sock->inData && sock->needy)
- if (!want.WriteDesc(typeEnumerated, &sock->key, 4))
- ++wantCount;
-
- sock = sock->next;
- }
-
- if (outputDirect.dataHandle) {
- err = AEPutParamDesc(output, '----', &outputDirect);
- AEDisposeDesc(&outputDirect);
-
- if (err)
- return;
- }
-
- err = outputData.CloseRecord() || outputData.Close(&desc);
-
- AEStream_Open(&outputData);
- outputData.OpenRecord(typeAERecord);
-
- if (outputDataCount) {
- outputDataCount = 0;
- if (!err) {
- err = AEPutParamDesc(output, 'OUTP', &desc);
- AEDisposeDesc(&desc);
-
- if (err)
- return; // Death before Dishonour
- } else
- return;
- }
-
- if (!want.CloseList() && wantCount) {
- if (!want.Close(&desc)) {
- AEPutParamDesc(output, 'WANT', &desc);
- AEDisposeDesc(&desc);
- }
- } else
- want.Close();
-
- if (sock)
- for (runs = 0; (runs += sock == first) < 2; sock = sock->next)
- if (sock->outData)
- SetHandleSize(sock->outData, 0);
- }
-
- MPAEVTSocket * MPAEVTSocketDomain::Lookup(OSType key, Boolean input, Boolean output)
- {
- int runs = 0;
- MPAEVTSocket * sock = first;
-
- if (sock)
- while ((runs += sock == first) < 2)
- if (sock->key == key) {
- if (input && !sock->inData)
- sock->inData = NewHandle(0);
- if (output && !sock->outData)
- sock->outData = NewHandle(0);
-
- return sock;
- } else
- sock = sock->next;
-
- return new MPAEVTSocket(key, input, output);
- }
-
- /********************* Interface routines **********************/
-
- pascal OSErr Relay(const AppleEvent * inData, AppleEvent * outData, long refCon)
- {
- ++MPAEVTSockets.received;
-
- if (inData)
- MPAEVTSockets.DistributeInput(inData, refCon);
- if (outData)
- MPAEVTSockets.CollectOutput(outData);
-
- return noErr;
- }
-
- pascal void FlushAEVTs(AppleEvent * outData)
- {
- if (outData)
- MPAEVTSockets.CollectOutput(outData);
- else {
- MPAEVTSockets.finish = true;
- MPAEVTSockets.DoRead();
- MPAEVTSockets.finish = false;
- }
- }
-
-